<!doctype html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>ChucK + JS => ChucKScript</title>
    <link rel="stylesheet" href="../../css/editor.css">
  </head>
  <body onresize="chuckEditor.resize();">
<div>
    <div id="program">// LiSa 1: pre-built files; this is an example that forces memory to grow
//-----------------------------------------------------------------------------
// name: aah.ck
// desc: Granular synthesis on "ahh"
//
// author: Jack Atherton
//
// Combining Dan Trueman's various helper scripts for Lisa
// https://github.com/ccrma/music220a/blob/master/chuck-examples/special/LiSa-SndBuf.ck
// http://chuck.cs.princeton.edu/doc/examples/special/LiSa-munger2.ck
// and adding several top level control variables
//-----------------------------------------------------------------------------

// PARAMS
class AhhSynth extends Chubgraph
{
    LiSa lisa => outlet;
    
    // spawn rate: how often a new grain is spawned (ms)
    25 =>  float grainSpawnRateMS;
    0 =>  float grainSpawnRateVariationMS;
    0.0 =>  float grainSpawnRateVariationRateMS;
    
    // position: where in the file is a grain (0 to 1)
    0.61 =>  float grainPosition;
    0.2 =>  float grainPositionRandomness;
    
    // grain length: how long is a grain (ms)
    300 =>  float grainLengthMS;
    10 =>  float grainLengthRandomnessMS;
    
    // grain rate: how quickly is the grain scanning through the file
    1.004 =>  float grainRate; // 1.002 == in-tune Ab
    0.015 =>  float grainRateRandomness;
    
    // ramp up/down: how quickly we ramp up / down
    50 =>  float rampUpMS;
    200 =>  float rampDownMS;
    
    // gain: how loud is everything overall
    1 =>  float gainMultiplier;
    
    float myFreq;
    fun float freq( float f )
    {
        f => myFreq;
        61 => Std.mtof => float baseFreq;
        // 1.002 == in tune for 56 for aah4.wav
        // 1.004 == in tune for 60 for aah5.wav
        myFreq / baseFreq * 0.98 => grainRate;
        
        return myFreq;
    }
    
    fun float freq()
    {
        return myFreq;
    }
    
    fun float gain( float g )
    {
        g => lisa.gain;
        return g;
    }
    
    fun float gain()
    {
        return lisa.gain();
    }
    
    
    
    SndBuf buf; 
    me.dir() + "aah5.wav" => buf.read;
    buf.length() => lisa.duration;
    // copy samples in
    for( int i; i < buf.samples(); i++ )
    {
        lisa.valueAt( buf.valueAt( i ), i::samp );
    }
    
    
    buf.length() => dur bufferlen;
    
    // LiSa params
    100 => lisa.maxVoices;
    0.1 => lisa.gain;
    true => lisa.loop;
    false => lisa.record;
    
    
    // modulate
    SinOsc freqmod => blackhole;
    0.1 => freqmod.freq;
    
    
    
    0.1 => float maxGain;
    
    fun void SetGain()
    {
        while( true )
        {
            maxGain * gainMultiplier => lisa.gain;
            1::ms => now;
        }
    }
    spork ~ SetGain();
    
    
    fun void SpawnGrains()
    {
        // create grains
        while( true )
        {
            // grain length
            ( grainLengthMS + Math.random2f( -grainLengthRandomnessMS / 2, grainLengthRandomnessMS / 2 ) )
            * 1::ms => dur grainLength;
            
            // grain rate
            grainRate + Math.random2f( -grainRateRandomness / 2, grainRateRandomness / 2 ) => float grainRate;
            
            // grain position
            ( grainPosition + Math.random2f( -grainPositionRandomness / 2, grainPositionRandomness / 2 ) )
            * bufferlen => dur playPos;
            
            // grain: grainlen, rampup, rampdown, rate, playPos
            spork ~ PlayGrain( grainLength, rampUpMS::ms, rampDownMS::ms, grainRate, playPos);
            
            // advance time (time per grain)
            // PARAM: GRAIN SPAWN RATE
            grainSpawnRateMS::ms  + freqmod.last() * grainSpawnRateVariationMS::ms => now;
            grainSpawnRateVariationRateMS => freqmod.freq;
        }
    }
    spork ~ SpawnGrains();
    
    // sporkee
    fun void PlayGrain( dur grainlen, dur rampup, dur rampdown, float rate, dur playPos )
    {
        lisa.getVoice() => int newvoice;
        
        if(newvoice > -1)
        {
            lisa.rate( newvoice, rate );
            lisa.playPos( newvoice, playPos );
            lisa.rampUp( newvoice, rampup );
            ( grainlen - ( rampup + rampdown ) ) => now;
            lisa.rampDown( newvoice, rampdown) ;
            rampdown => now;
        }
    }


}

LPF lpf => NRev rev => dac;
7000 => lpf.freq;
rev.mix(0.1);

57 => int A3;
59 => int B3;
61 => int Cs4;
64 => int E4;
66 => int Fs4;
68 => int Gs4;
71 => int B4;
73 => int Cs5;
75 => int Ds5;
78 => int Fs5;
80 => int Gs5;
83 => int B5;
E4 - 12 => int E3;
E4 + 12 => int E5;
Gs4 - 12 => int Gs3;


// the notes
[ 
  [A3, E4, Cs5, Gs5],
  [E3, B4, Ds5, Gs5],
  [Cs4 - 12, Gs4, E5, B5],
  [Gs3, Cs4, B4, Fs5]
] @=> int notes[][];


AhhSynth ahhs[ notes[0].size() ];
for( int i; i < ahhs.size(); i++ )
{
    0.8 / ahhs.size() => ahhs[i].gain;
    ahhs[i] => lpf;
}

// wait 1 second for crappy audio of memory expanding to disappear
0 => rev.gain;
1::second => now;
1 => rev.gain;

while( true )
{
    for( int i; i < notes.size(); i++ )
    {
        for( int j; j < notes[i].size(); j++ )
        {
            notes[i][j] - 12 => Std.mtof => ahhs[j].freq;
        }
        
        2::second => now;
    }
}
        
</div>
</div>
<div class="buttons">
    <button class="chuckButton" id="startChuck" disabled="disabled">Start ChucK</button>
    <button class="chuckButton" id="compileButton" disabled="disabled">Compile and Run</button>
    <button class="chuckButton" id="replaceButton" disabled="disabled">Replace</button>
    <button class="chuckButton" id="removeButton" disabled="disabled">Remove</button>
    <button class="chuckButton" id="clearButton" disabled="disabled">Clear VM and Globals</button>
    <button class="chuckButton" id="micButton" disabled="disabled">Enable Microphone</button>
    <button class="chuckButton" id="fileFormButton">Show File Uploader</button>
</div>
<div id="fileForm" class="hidden">
    <p>File name to use in ChucK: <input type="text" id="uploadFilename"></p>
    <p>Local file to upload: <input type="file" id="uploadFile" name="uploadFile"></p>
    <p><button class="chuckButton" id="fileButton" disabled="disabled">Upload File to Virtual File System</button></p>
</div>
<div>
    <textarea id="output"></textarea>
    <div id="shreds">
        <table id="shredstable">
            <col width="60px">
            <col width="1*">
            <col width="50px">
            <col width="60px">
            <tr><th>shred</th><th>code</th><th>time</th><th>remove</th>
        </table>
    </div>
    <div class="clearfix"></div>
</div>
    <script type="text/javascript" src="../../js/ace-min-noconflict/ace.js" charset="utf-8"></script>
    <script type="text/javascript" src="../../js/editor.js"></script>
    <script type="text/javascript" src="../../js/defer.js"></script>
    <script>
        // override
        var whereIsChuck = "../../js";
        
        var serverFilesToPreload = [
            {
                serverFilename: '../../wav/aah.wav',
                virtualFilename: 'aah5.wav'
            }
        ];
    </script>
    <script type="text/javascript" src="../../js/webchuck_host.js"></script>
    <script type="text/javascript" src="../../js/host.js"></script>
  </body>
</html>


